#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "cmd_core.h"
#include "cmd_core_utils.h"

static int init_module_list(list_t **module_list);
static list_t *g_module_list = NULL;


int init_module_core()
{
	return init_module_list(&g_module_list);
}

static int init_module_list(list_t **module_list)
{	
	*module_list = list_new();
	if(*module_list != NULL){
		(*module_list)->match = match_module;
		(*module_list)->print = print_module;
		return 0;
	}else{
		return -1;
	}
}

int register_module(module_node_t *module_node)
{	
	list_node_t *new_node = list_node_new((void *)module_node);
	if(new_node){
		list_rpush(g_module_list, new_node);
	}else{
		return -1;
	}
	return 0;
}

int register_module_byname(char *module_name)
{	
	module_node_t *cur;

	cur = (module_node_t *)malloc(sizeof(module_node_t));

	if(!cur){
		return -1;
	}

	memset(cur, 0, sizeof(module_node_t));
	//printf("module is %s\n",*module_name);
	strncpy(cur->module_name, module_name, MAX_MODULE_NAME_LEN);

	if(init_cmd_list(&(cur->cmd_list))){
		goto FAIL;
	}
	if(register_module(cur)){
		goto FAIL;
	}

	return 0;

	FAIL:
	free(cur);
	return -2;
}

#if 0
/**
 * module_node need be inited before passing to this function.
 */
int register_modules_auto_end(module_node_t *module_node)
{
	size_t i;
	module_node_t *cur = module_node;
	for(i = 0; strcmp(cur->module_name,""); cur++,i++){
		//printf("cur->cmd is %s\n",cur->cmd);
		if(register_module(cur)){
			return -1;
		}
	}
	return i;
}
#endif

int register_modules_auto_end_byname(char **module_name)
{
	int i;	
	module_node_t *cur;
	for(i = 0; /*strcmp(*module_name,"")*/*module_name != NULL; module_name++,i++){
		cur = (module_node_t *)malloc(sizeof(module_node_t));
		if(!cur){
			return -1;
		}
		memset(cur, 0, sizeof(module_node_t));
		//printf("module is %s\n",*module_name);
		strncpy(cur->module_name, *module_name, MAX_MODULE_NAME_LEN);

		if(init_cmd_list(&(cur->cmd_list))){
			goto FAIL;
		}
		if(register_module(cur)){
			goto FAIL;
		}
	}
	//printf("i = %d\n",i);
	return i;

	FAIL:
	free(cur);
	return -2;
}


int init_cmd_list(list_t **cmd_list)
{	
	*cmd_list = list_new();
	if(*cmd_list != NULL){
		(*cmd_list)->match = match_cmd;
		(*cmd_list)->print = print_cmd;
		return 0;
	}else{
		return -1;
	}
}

int register_cmd(list_t *cmd_list, cmd_process_t *cmd_process)
{	
	list_node_t *new_node = list_node_new((void *)cmd_process);

	if(new_node){
		list_rpush(cmd_list, new_node);
	}else{
		return -1;
	}
	return 0;
}

#if 0
int register_cmds(list_t *cmd_list,cmd_process_t *cmd_process, size_t num)
{
	size_t i;
	cmd_process_t *cur = cmd_process;
	for(i = 0; i < num; cur++,i++){
		if(register_cmd(cmd_list, cur)){
			return -1;
		}
	}
	return i;
}
#endif

int register_cmds_auto_end(char *module_name, cmd_process_t *cmd_process)
{
	size_t i;
	list_node_t *module_node;
	list_t *cmd_list;
	cmd_process_t *cur;
	cmd_process_t *origin = cmd_process;

	module_node = find_module_node_in_list(g_module_list, module_name);
	if(module_node == NULL){
		return -1;
	}
	cmd_list = ((module_node_t *)module_node->val)->cmd_list;

	for(i = 0; origin->exec_fun != NULL; origin++,i++){		
		cur = (cmd_process_t *)malloc(sizeof(cmd_process_t));
		if(!cur){
			return -1;
		}
		memcpy(cur, origin, sizeof(cmd_process_t));
		//printf("cur->cmd is %s\n",origin->cmd);
		if(register_cmd(cmd_list,cur)){
			return -2;
		}
	}
	return i;
}

#if 0
int cmd_core_process_flow(cmd_process_t *cur_cmd_process, char *cmd_line, char *ret_buf, size_t ret_max_len)
{
	int ret = 0;
	ret = cur_cmd_process->parse_fun(cmd_line, ret_buf, ret_max_len);
	if(ret < 0){
		return ret;
	}

	ret = cur_cmd_process->exec_fun(ret_buf, ret_max_len);
	if(ret < 0){
		return ret;
	}
	return ret;
}
#endif

int execute(char *cmd_line, char *ret_buf, size_t ret_max_len)
{
	int ret;
	list_node_t *match_node;
	module_node_t *match_module_node;
	char *cur_cmd = cmd_line;
	char *cur_buf;

	trimrightblank(cur_cmd);
	cur_buf = get_delimited_string_segment(cur_cmd, ' ');

	/* exit */
	if(!strcmp(cur_buf,"exit")){
		exit(0);
	}

	match_node = find_module_node_in_list(g_module_list, cur_buf);
	if(!match_node){
		goto FAIL;
	}

	match_module_node = (module_node_t *)match_node->val;

	ret = execute_cmd_line(match_module_node->cmd_list, 
			jump_to_next_delimited_segment(cur_cmd, ' '), ret_buf, ret_max_len);

	realse_delimited_string_segment(cur_buf);
	return ret;

	FAIL:
	realse_delimited_string_segment(cur_buf);
	return -1;

}


int execute_cmd_line(list_t *cmd_list, char *cmd_line, char *ret_buf, size_t ret_max_len)
{
	int ret;
	list_node_t *match_node;
	cmd_process_t *match_cmd_process;
	char *cur_cmd = cmd_line;

	char *cur_buf = get_delimited_string_segment(cur_cmd, ' ');

	match_node = find_cmd_node_in_list(cmd_list,cur_buf);

	if(!match_node){
		goto FAIL;
	}

	match_cmd_process = (cmd_process_t *)match_node->val;
/*
	if(match_cmd_process->parse_fun(cur_cmd)){
		perror("parse cmd Fail\n");
		goto FAIL;
	}
*/
	ret = match_cmd_process->exec_fun(jump_to_next_delimited_segment(cur_cmd, ' '), ret_buf, ret_max_len);

	realse_delimited_string_segment(cur_buf);
	return ret;

	FAIL:
	realse_delimited_string_segment(cur_buf);
	return -1;
}

list_node_t * find_module_node_in_list(list_t *module_list, char *module)
{
	module_node_t tmp;

	strncpy(tmp.module_name,module,MAX_MODULE_NAME_LEN);

	return list_find(module_list, &tmp);
}


list_node_t * find_cmd_node_in_list(list_t *cmd_list, char *cmd)
{
	cmd_process_t tmp;

	strncpy(tmp.cmd,cmd,MAX_CMD_LEN);
	return list_find(cmd_list, &tmp);
}


int match_cmd(void *a, void *b)
{
	cmd_process_t *val1 = (cmd_process_t *)a;
	cmd_process_t *val2 = (cmd_process_t *)b;

	if(!strcmp(val1->cmd,val2->cmd)){
		return 1;
	}else{
		return 0;
	}
}

int match_module(void *a, void *b)
{
	module_node_t *val1 = (module_node_t *)a;
	module_node_t *val2 = (module_node_t *)b;

	if(!strcmp(val1->module_name,val2->module_name)){
		return 1;
	}else{
		return 0;
	}

}

void print_module(void *val)
{
	printf("%s\n",((module_node_t *)val)->module_name);
}

void print_cmd(void *val)
{
	printf("%s\n",((cmd_process_t *)val)->cmd);
}


///TODO:test
void release_list(list_t **list)
{
	list_destroy(*list);
	*list = NULL;
}
